package com.example.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.utils.StringUtils;
import com.example.constant.CacheNameConstant;
import com.example.constant.TokenConstant;
import com.example.exception.TokenExpiredException;
import com.example.exception.TokenValidationException;
import com.example.pojo.SecureUser;
import com.example.pojo.SecureUserToken;
import com.example.utils.ResponseUtil;
import com.example.utils.TokenUtil;
import com.example.web.domain.Result;
import com.example.web.domain.ResultCode;
import lombok.SneakyThrows;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;

@Component
@Order(-110)
//设置执行优先级，在 全局权限认证过滤器 之前执行
public class AuthenticationFilter implements GlobalFilter, InitializingBean {


    @Autowired
    RedisTemplate<String,String> redisTemplate;

    private static Set<String> shouldSkipUrl = new LinkedHashSet<>();

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        //获取request请求
        String requestPath = exchange.getRequest().getURI().getPath();

        if (shouldSkip(requestPath)) {
            return chain.filter(exchange);
        }

        //获取Authorization请求头
        String tokenHeaderKey = exchange.getRequest().getHeaders().getFirst("Authorization-Key");

        String tokenHeader = exchange.getRequest().getHeaders().getFirst("Authorization");

        //Authorization请求头为空
        if(StringUtils.isEmpty(tokenHeaderKey)) {
            return ResponseUtil.out(exchange.getResponse(),Result.failure(ResultCode.NOT_LOGIN));
        }
        // 根据token获取当前用户对象
        SecureUser secureUser;
        SecureUserToken userToken;
        try {
            userToken =verifyToken(tokenHeaderKey,tokenHeader.replaceFirst(TokenConstant.TOKEN_PREFIX, ""),exchange.getResponse());
            secureUser = userToken.getSecureUser();
        }catch (TokenValidationException e){
            return ResponseUtil.out(exchange.getResponse(),Result.failure(ResultCode.TOKEN_INVALID));
        }catch (TokenExpiredException e){
            return ResponseUtil.out(exchange.getResponse(),Result.failure(ResultCode.TOKEN_EXPIRED));
        }

        // 讲当前用户信息简单加密，传给其他微服务
        String jsonToken = Base64.encodeBase64String(JSON.toJSONString(userToken).getBytes());

        // 讲加密后的信息添加到请求头
        ServerHttpRequest request = exchange.getRequest().mutate().header(TokenConstant.SERVICE_TOKEN, jsonToken).build();

        // return UsernamePasswordAuthenticationToken
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(secureUser, secureUser.getId(), secureUser.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);

        return chain.filter(exchange.mutate().request(request).build());
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // 在类被初始化完成时，把不拦截认证的请求放入集合
        shouldSkipUrl.add("/api/login");
        shouldSkipUrl.add("/api/logout");
    }

    public SecureUserToken verifyToken(String key, String token, ServerHttpResponse response){
        SecureUserToken secureUserToken = taskToken(key);
        if(secureUserToken == null) {
            throw new TokenExpiredException("token expired");
        }
        TokenUtil.parse(response,token);
        return secureUserToken;
    }

    public SecureUserToken taskToken(String key){
        // 获取json字符串
        String userStr = redisTemplate.opsForValue().get(CacheNameConstant.TOKEN_NAME_PREFIX + key);

        String substring = userStr.substring(1, userStr.length() - 1);

        String replace = substring.replace("\\", "");
        // 反序列化，但authorities反序列化失败,需要手动注入
        SecureUserToken secureUserToken = JSONObject.parseObject(replace, SecureUserToken.class);

        // 获取jsonAuthorities
        JSONArray jsonAuthorities = JSONObject.parseObject(replace).getJSONObject("secureUser").getJSONArray("authorities");

        // 循环注入当前用户权限
        HashSet<SimpleGrantedAuthority> authorities=new HashSet<>();
        for (int i = 0; i < jsonAuthorities.size(); i++) {
            authorities.add(new SimpleGrantedAuthority(jsonAuthorities.getJSONObject(i).get("authority").toString()));
        }
        secureUserToken.getSecureUser().deserializeAuthorities(authorities);

        return secureUserToken;
    }

    private boolean shouldSkip(String reqPath) {

        for(String skipPath:shouldSkipUrl) {
            if(reqPath.contains(skipPath)) {
                return true;
            }
        }
        return false;
    }

}

